#pragma once

#include "Requester.hpp"

namespace rpc
{
    namespace client
    {
        class ServiceProvider
        {
        public:
            using ptr = std::shared_ptr<ServiceProvider>;
            ServiceProvider(const Requester::ptr &requester)
                : _requester(requester)
            {
            }

            bool registryService(const BaseConnection::ptr &conn, const Address &host, const std::string service)
            {
                auto service_req = MessageFactory::create<ServiceRequest>();
                service_req->setId(UUID::uuid());
                service_req->setHost(host);
                service_req->setMethod(service);
                service_req->setOption(SERVICE_OP_TYPE::SERVICE_REGISTER);
                auto msg_req = std::dynamic_pointer_cast<BaseMessage>(service_req);

                BaseMessage::ptr msg_rsp;
                bool ret = _requester->send(conn, msg_req, msg_rsp);
                if (ret == false)
                {
                    ELOG("service registry send error");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (service_rsp.get() == nullptr)
                {
                    ELOG("service response type down error");
                    return false;
                }
                return true;
            }

        private:
            Requester::ptr _requester;
        };

        class ServiceHosts
        {
        public:
            using ptr = std::shared_ptr<ServiceHosts>;
            ServiceHosts() {}

            ServiceHosts(const std::vector<Address> &hosts)
                : _hosts(hosts), _idx(0)
            {
            }

            void appendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }

            void removeHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }

            Address chooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _idx++ % _hosts.size();
                return _hosts[pos];
            }

            bool empty()
            {
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _idx; // 没初始化
            std::vector<Address> _hosts;
        };

        class ServiceDiscoverer
        {
        public:
            using ptr = std::shared_ptr<ServiceDiscoverer>;
            using offline_cb = std::function<void(const Address&)>;
            ServiceDiscoverer(const Requester::ptr &requester, const offline_cb &cb)
                : _requester(requester), _offline_callback(cb)
            {
            }

            bool serviceDiscovery(const BaseConnection::ptr &conn, const std::string &service, Address &host)
            {
                //  _service_map_hosts中存在请求的服务
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _service_map_hosts.find(service);
                    if (it != _service_map_hosts.end())
                    {
                        if (it->second->empty() != true)
                        {
                            host = it->second->chooseHost();
                            return true;
                        }
                    }
                }
                // 2. 当前不存在被请求的服务，进行服务发现
                auto service_req = MessageFactory::create<ServiceRequest>();
                service_req->setId(UUID::uuid());
                service_req->setMsgType(MSG_TYPE::REQ_SERVICE);
                service_req->setOption(SERVICE_OP_TYPE::SERVICE_DISCOVERY);
                service_req->setMethod(service);
                auto msg_req = std::dynamic_pointer_cast<BaseMessage>(service_req);

                auto msg_rsp = MessageFactory::create<BaseMessage>();
                bool ret = _requester->send(conn, msg_req, msg_rsp);
                if (ret == false)
                {
                    ELOG("service discover send error");
                    return false;
                }
                auto service_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg_rsp);
                if (service_rsp.get() == nullptr)
                {
                    ELOG("service response type down error");
                    return false;
                }
                if (service_rsp->rcode() != RCODE::RCODE_OK)
                {
                    ELOG("service discover error");
                    return false;
                }

                auto hosts = service_rsp->hosts();
                if (hosts.empty() == true)
                {
                    ELOG("not found current service");
                    return false;
                }
                // 发现了对应的服务
                std::unique_lock<std::mutex> lock(_mutex);

                auto hosts_ptr = std::make_shared<ServiceHosts>(hosts);
                host = hosts_ptr->chooseHost();
                _service_map_hosts[service] = hosts_ptr;
                return true;
            }

            // 提供给Dispatcher模块处理服务上、下线请求的回调函数
            void onServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &msg)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto operate_type = msg->Option();
                std::string service = msg->Method();

                // 服务上线 —— 将对应的主机地址信息添加到 _service_map_hosts 中
                if (operate_type == SERVICE_OP_TYPE::SERVICE_ONLINE)
                {
                    auto it = _service_map_hosts.find(service);
                    Address host = msg->host();
                    if (it == _service_map_hosts.end()) // 当前客户端不存在service服务
                    {
                        auto hosts_ptr = std::make_shared<ServiceHosts>();
                        hosts_ptr->appendHost(host);
                        _service_map_hosts[service] = hosts_ptr;
                    }
                    else
                    {
                        it->second->appendHost(host);
                    }
                }
                else if (operate_type == SERVICE_OP_TYPE::SERVICE_OFFLINE)
                {
                    auto it = _service_map_hosts.find(service);
                    if (it == _service_map_hosts.end())
                    {
                        return; // 不存在service服务，不需要进行服务下线操作
                    }
                    Address host = msg->host();
                    it->second->removeHost(host);
                    _offline_callback(host);
                }
                // 其他操作类型不做处理
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, ServiceHosts::ptr> _service_map_hosts;
            Requester::ptr _requester;
            offline_cb _offline_callback;
        };
    }
}